using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

using Microsoft;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;




//TODO: Be able to turn on/off support for FAQ/User-defined markup functionality via PropertyBag
//TODO: Hide Rich Text Toolbar (since we're now supporting wiki markup)
//TODO: Be able to mark some text as literal - i.e. don't do any token replacement
//TODO: Code Access Security



namespace CKS.EWE.Controls
{
    /// <summary>
    /// CLASS: EWERichTextField
    /// DESCRIPTION: Custom implementation of RichText field to support wiki markup parsing
    /// DATE: September 16, 2007
    /// AUTHOR: David Mann
    /// HISTORY: Beta Version
    /// NOTES: Incorporating FAQ functionality written by Loke Kit Kai
    /// </summary>
    [ToolboxData("<{0}:EWERichTextField runat=server></{0}:EWERichTextField>")]
	[SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true), AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal), SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true), AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    public class EWERichTextField : RichTextField
	{
        /// <summary>
        /// structure for tracking information about each Token
        /// </summary>
                   
  
#region RichTextField Overridden Methods
        protected override void RenderFieldForDisplay(HtmlTextWriter output)
        {
            string HtmlValue = string.Empty;

            #region default processing for [[wiki_links]]
            System.IO.StringWriter sw = new System.IO.StringWriter();
            HtmlTextWriter wrtr = new HtmlTextWriter(sw);
            base.RenderFieldForDisplay(wrtr);
            HtmlValue = sw.ToString();
            sw.Dispose();
            wrtr.Dispose();
            #endregion



            #region FAQ Link Parsing
            if (WikiInstanceSupportsFAQ())
            {
                //only parse for FAQ links if this wiki list is tagged to support it
                HtmlValue = ParseFAQMarkup(HtmlValue);
            }

            #endregion

            #region Simple Token Parsing
            List<TokenUtils.WikiToken> WikiTokens = new List<TokenUtils.WikiToken>();
            TokenUtils.WikiToken wt;
            WikiTokens.AddRange(TokenUtils.GetDefaultWikiTokens());

            if (WikiInstanceSupportsCustomTokens())
            {
                SPListItemCollection lic = GetTokensFromList();
                if (
                    (lic != null)
                    && (lic.Count > 0))
                {
                    foreach (SPListItem li in lic)
                    {

                        //TODO: All SPList-based tokens should start with a common delimiter (e.g ~) to help differentiate them?
                        wt = new TokenUtils.WikiToken();
                        wt.sBeginMarkup = li["BeginMarkup"].ToString();
                        wt.sEndMarkup = li["EndMarkup"].ToString();
                        wt.sBeginToken = li["BeginToken"].ToString();
                        wt.sEndToken = li["EndToken"].ToString();
                        WikiTokens.Add(wt);
                    }
                }
            }
            foreach (TokenUtils.WikiToken oneToken in WikiTokens)
            {
                HtmlValue = ParseSimpleToken(HtmlValue, oneToken.sBeginToken, oneToken.sEndToken, oneToken.sBeginMarkup, oneToken.sEndMarkup);
            }

            #endregion

            #region Table Parsing
            int iTableStart = HtmlValue.IndexOf("{table}");

            while (iTableStart > -1)
            {
                HtmlValue = ParseTableMarkup(HtmlValue, iTableStart);
                iTableStart = HtmlValue.IndexOf("{table}");

            }
            #endregion

            
            HtmlValue = FixNewPageLinks(HtmlValue);


            output.Write(HtmlValue);

        }

        protected override void RenderFieldForInput(HtmlTextWriter output)
        {
            base.RenderFieldForInput(output);
//            SPFieldMultiLineText field = (SPFieldMultiLineText)base.Field;
//            if (field.WikiLinking)
//            {
//                //output.Write("<br><span class=ms-formdescription>");
//                //output.Write("To place the FAQ table of content, type {{TOC}} exactly as shown.");
//                //output.Write("</span>");
//                //output.Write("<br><span class=ms-formdescription>");
//                //output.Write("To create a FAQ Question, type [{Question}]");
//                //output.Write("</span>");
////                string sMarkUpDiv = @"<div id='wikiMarkup' 
////                    style='visibility:visible; width:150px; top:291px; position:absolute; '>
////                    <span style='font-size:larger; text-decoration:underline; width:100%; 
////                    text-align:center'>Supported Markup</span>";
////                output.Write(sMarkUpDiv);
//            }
        }

        [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
        public override void Validate()
        {
            if ((base.ControlMode != SPControlMode.Display) && base.IsValid)
            {
                base.Validate();
                if (base.Field.Required && (base.Text == "<DIV></DIV>"))
                {
                    base.IsValid = false;
                    base.ErrorMessage = SPResource.GetString("MissingRequiredField", new object[0]);
                }
            }
        }

        protected override string DefaultTemplateName
        {
            get
            {
                return "RichTextField";
            }
        }

#endregion RichTextField Overridden Methods

#region Support for Custom Tokens

        /// <summary>
        /// Retrieve admin-added Tokens from SPList
        /// </summary>
        /// <returns>SPListItemCollection of Tokens</returns>
        private SPListItemCollection GetTokensFromList()
        {
             
            SPListItemCollection lic = null;
            SPWeb web = SPContext.Current.Web;
            
            SPList lstMarkup = GetList(web,this.List.Title + "_WikiMarkup");
            if (lstMarkup != null)
            {
                SPQuery qry = new SPQuery();
                qry.Query = "<Where><Eq><FieldRef Name='Active' /><Value Type='Boolean'>1</Value></Eq></Where>";
            
                lic = lstMarkup.GetItems(qry);
            }
            return lic;
        }

        /// <summary>
        /// Return the desired SPList without throwing an error if it doesn't exist
        /// </summary>
        /// <param name="web">SPWeb from which to retrieve SPList</param>
        /// <param name="sListTitle">Title of the SPList to retrieve</param>
        /// <returns>SPList matching specified Title</returns>
        private SPList GetList(SPWeb web, string sListTitle)
        {
            SPList lstRetVal = null;
            foreach (SPList lst in web.Lists)
            {
                if (lst.Title == sListTitle)
                {
                    lstRetVal = lst;
                    break;
                }
            }
            return lstRetVal;
        }
#endregion Support for Custom Tokens
  
#region Configuration Support
        /// <summary>
        /// Check whether FAQ Markup is supported in this Wiki instance
        /// </summary>
        /// <returns>Boolean</returns>
        bool WikiInstanceSupportsFAQ()
        {
            bool boolSupportFAQ = true;
            //temporarily commented out - add back in when Properties are added
            //try
            //{
            //    SPWeb web = SPContext.Current.Web;
            //    boolSupportFAQ = System.Convert.ToBoolean(web.Properties[this.List.Title + "_supportFAQs"]);
            //}
            //catch
            //{}
            return boolSupportFAQ;
        }

        /// <summary>
        /// Check whether custom defined markup is supported in this Wiki instance
        /// </summary>
        /// <returns>Boolean</returns>
        private bool WikiInstanceSupportsCustomTokens()
        {
            bool boolSupportCustomTokens = true;


            //temporarily commented out - add back in when Properties are added
            //try
            //{
            //    SPWeb web = SPContext.Current.Web;
            //    boolSupportCustomTokens = System.Convert.ToBoolean(web.Properties[this.List.Title + "_supportCustomTokens"]);
            //    //temp:
            //    boolSupportCustomTokens = true;
            //}
            //catch
            //{ }
            return boolSupportCustomTokens;
        }

#endregion Configuration Support

#region Misc. Support Routines
        /// <summary>
        /// Make sure links to create new pages go to our customized page instead of the default
        /// </summary>
        /// <param name="sInputString">Fully parsed string of HTML representing our wiki page</param>
        /// <returns>String of HTML with links corrected</returns>
        private string FixNewPageLinks(string sInputString)
        {
            return sInputString.Replace(@"_layouts/CreateWebPage.aspx", @"_layouts/CKSEWE/CreateEWEPage.aspx");
        }

       
#endregion Misc. Support Routines

#region Parsing SubRoutines

        
        /// <summary>
        /// Replace Tokens to convert default and custom Tokens to valid HTML
        /// </summary>
        /// <param name="sInputString">string of HTML representing our wiki page</param>
        /// <param name="sStartDelim">beginning of token</param>
        /// <param name="sEndDelim">end of token</param>
        /// <param name="sStartReplacementText">string to put in place of beginning token</param>
        /// <param name="sEndReplacementText">string to put in place of end token</param>
        /// <returns>string of HTML representing our wiki page with default and custom Token Markup converted to valid HTML</returns>
        protected string ParseSimpleToken(string sInputString, string sStartDelim, string sEndDelim, string sStartReplacementText, string sEndReplacementText)
        {
            //Can't use regular expressions due to conflicts between regex metacharacters and potential tokens
            
            StringBuilder sbHTML = null ;
            string sStartToken = sStartDelim;
            string sEndToken = sEndDelim;
            int iEndDelimLength = 0;
            int iTokenEnd = 0;
            int iTokenStart = 0;
            switch (sEndDelim.ToLower())
            {
                case "end of paragraph":
                    sEndToken = "\r\n";
                    sEndDelim = "\r\n";
                    iEndDelimLength = 2;
                    break;
                
                default:
                    break;
            }
            iTokenStart = sInputString.IndexOf(sStartToken);

            string sTemp = default(string);

            while (iTokenStart > 0)
            {
                iTokenEnd = sInputString.IndexOf(sEndToken, iTokenStart + 1);



                    if (
                        (sInputString.Substring(iTokenStart, 8).ToLower() != "_layouts")
                        && (sInputString.Substring(iTokenStart - 2, 3).ToLower() != "ms-")
                        )
                    {
                        //holy crap, what a hack.  Need to keep _layouts and css classes beginning ms- from triggering 
                        //a replacement when _  or - are used as a token...
                        //any others to worry about???  Gotta think of a better way...

                        if (iTokenEnd > iTokenStart)
                        {
                            sbHTML = new StringBuilder();
                            sbHTML.Append(sInputString.Substring(0, iTokenStart)); //add to start delimiter
                            sbHTML.Append(sStartReplacementText); //add replacement text
                            sTemp = sInputString.Substring(iTokenStart + sStartDelim.Length, iTokenEnd - iTokenStart - 1);
                            sbHTML.Append(sInputString.Substring(iTokenStart + sStartDelim.Length, (iTokenEnd - iTokenStart) - sStartDelim.Length )); //add to end delimiter
                            sbHTML.Append(sEndReplacementText);  //add end replacement text
                            sbHTML.Append(sInputString.Substring(iTokenEnd + sEndDelim.Length - iEndDelimLength)); //add remainder of sInputText
                            sInputString = sbHTML.ToString();
                            
                        }
                        else
                        {
                            // EndToken was not found - assume end of wiki text...
                            sbHTML = new StringBuilder();
                            sbHTML.Append(sInputString.Substring(0, iTokenStart)); //add to start delimiter
                            sbHTML.Append(sStartReplacementText); //add replacement text
                            sbHTML.Append(sInputString.Substring(iTokenStart + sStartDelim.Length)); //add to end of wiki text
                            sbHTML.Append(sEndReplacementText);  //add end replacement text
                            //sbHTML.Append(sInputString.Substring(iTokenEnd + sEndDelim.Length - iEndDelimLength)); //add remainder of sInputText
                            sInputString = sbHTML.ToString();

                        }
                    }
                    
                if (iTokenEnd == -1)
                { iTokenStart = 0; }
                else
                {
                   
                    iTokenStart = sInputString.IndexOf(sStartToken, iTokenEnd + 1);
                }
            }
            
                return sInputString;
            
        }

        /// <summary>
        /// Replace Tokens to convert FAQ Wiki Markup to valid HTML
        /// </summary>
        /// <param name="sInputString">string of HTML representing our wiki page</param>
        /// <returns>string of HTML representing our wiki page with FAQ Wiki Markup converted to valid HTML</returns>
        protected string ParseFAQMarkup(string sInputString)
        {
            int HrefIndex = 0;
            int StartHrefIndex = 0;
            int EndHrefIndex = 0;

            string TOC = string.Empty;
            HrefIndex = 0;
            int NumQuestions = 0;


            while (true)
            {
                StartHrefIndex = sInputString.IndexOf("[{", HrefIndex);
                if (StartHrefIndex > 0)
                {
                    EndHrefIndex = sInputString.IndexOf("}]", HrefIndex);
                    if (EndHrefIndex <= StartHrefIndex)
                    {
                        break;
                    }
                    NumQuestions = NumQuestions + 1;
                    string Question = sInputString.Substring(StartHrefIndex, EndHrefIndex - StartHrefIndex + 2);
                    TOC = TOC + "<p><a class=wikilink href=#Question" + NumQuestions + ">" + Question.Substring(2, Question.Length - 4) + "</a></p>";
                    string NewQuestion = Question.Replace("[{", "<a style='float:right;position:relative' href=#top>Back to Top</a><a name=Question" + NumQuestions + "></a>").Replace("}]", "");
                    sInputString = sInputString.Replace(Question, NewQuestion);
                    HrefIndex = StartHrefIndex + NewQuestion.Length;
                }
                else
                {
                    break;
                }
            }
            TOC = "<a name=Top></a>" + TOC;
            sInputString = sInputString.Replace("{{TOC}}", TOC);
            return sInputString;
        }

        /// <summary>
        /// Replace Tokens to convert Wiki Markup for tables to valid HTML
        /// </summary>
        /// <param name="sInputString">string of HTML representing our wiki page</param>
        /// <param name="iTableStart">character position of beginning of Table in WikiMarkup</param>
        /// <returns>string of HTML representing our wiki page with table beginning at iTableStart converted to valid HTML</returns>
        private string ParseTableMarkup(string sInputString, int iTableStart)
        {
            StringBuilder sbTableString = new StringBuilder();
            int iTableEnd = sInputString.IndexOf("{table}", iTableStart + 7);
            int iTableLength = iTableEnd - iTableStart;
            string sTableMarkup = sInputString.Substring(iTableStart, iTableLength);

            sbTableString.Append("<table cellpadding='0' cellspacing='0' border='0'>");


            //begin processing row
            int iTableRowEnd = 0;
            int iTableRowStart = sTableMarkup.IndexOf("||");
            while (iTableRowStart > 0)
            {
                iTableRowEnd = sTableMarkup.IndexOf("\r\n", iTableRowStart);
                if (iTableRowEnd == -1)
                {
                    iTableRowEnd = sTableMarkup.IndexOf("<br>", iTableRowStart);
                }
                string sRow = "<tr><td>" + sTableMarkup.Substring(iTableRowStart + 2, iTableRowEnd - iTableRowStart) + "</td></tr>";
                sRow = sRow.Replace("||", "</td><td>");
                sRow = sRow.Replace("</div>", "");
                sRow = sRow.Replace("\r\n", "");
                sbTableString.Append(sRow);
                sTableMarkup = sTableMarkup.Substring(iTableRowEnd);
                iTableRowStart = sTableMarkup.IndexOf("||");
            }
            sbTableString.Append("</table>");
            sInputString = sInputString.Remove(iTableStart, iTableEnd - iTableStart + 7);
            sInputString = sInputString.Insert(iTableStart, sbTableString.ToString());

            //TODO: Clean Up Table parsing and support basic styling via attributes on opening {table}, i.e {table border=0, rowstyle="...", cellstyle="..."}, etc.

            return sInputString;
        }
#endregion Parsing SubRoutines

   
	}
}
